/*
 * Individuo.cpp
 *
 *  Created on: 18/10/2012
 *      Author: vagner
 */

#include "Individuo.h"
#include <iostream>
#include <ctime>
#include <cstdlib>
#include <algorithm>
#include "VerticesCandidatos.h"
#include <sys/time.h>

namespace computacaoNatural {

Individuo::Individuo() {
	// Do nothing

}

Individuo::Individuo(int ID, int numMedianas, int numVertices) {

	if (numMedianas > 0 && numVertices > 0) {
		this->ID = ID;
		this->tamGenotipo = numMedianas;
		sorteia(numMedianas, numVertices, this->pGenotipo);
		this->pMatrizDemanda = new MatrizDemanda(numVertices, numVertices);
		this->fitness = 0.0;
	} else {
		std::cout << "Parametro invalido. Programa será encerrado."
				<< std::endl;
		_exit(-1);
	}

}

double Individuo::getFitness() {
	return this->fitness;
}

inline void Individuo::sorteia(const int& p, const int& n,
		std::vector<int>& genotipo) {

	timeval time;
	gettimeofday(&time, NULL);
	std::srand(time.tv_sec * time.tv_usec);
	int num_random;

	while (int(genotipo.size()) < p) {

		if (n == 0 || n < 0) {
			genotipo.push_back(0);
		} else {
			num_random = std::rand() % n;
			//std::cout << "Numero gerado: " << num_random << std::endl;
			if (!findInVector(genotipo, num_random)) {
				//std::cout << "Numero inserido: " << num_random << std::endl;
				genotipo.push_back(num_random);
				if ((int) genotipo.size() > this->tamGenotipo) {
					std::cout << "Erro de insercao. O tamanho do genotipo é: "
							<< genotipo.size() << " Medoto Individuo::sorteia" << std::endl;
					_exit(-1);
				}
			}
		}
	}

}

inline bool Individuo::findInVector(const std::vector<int>& Lista,
		int item) const {

	return (std::find(Lista.begin(), Lista.end(), item) != Lista.end());
}

void Individuo::show() const {

	std::cout << "#" << ID << std::endl;
	std::cout << "Fenotipo: ";
	for (size_t i = 0; i < pGenotipo.size(); i++) {
		std::cout << pGenotipo.at(i) << " ";
	}
	std::cout << std::endl;

	std::cout << "Fitness: " << this->fitness << std::endl;
}

void Individuo::calculaFitness(Grafo* G) {
	std::vector<int>::iterator it;
	int capacidadeVertice;
	int demandaVertice;
	candidato c;
	int numVertices;
	int atendimento; //Variavel que 1 caso a mediana m atenda o vertice v;

	std::vector<candidato> candidatos;

	this->fitness = 0.0;

	//std::cout << "Fitness Inicial " << this->fitness << std::endl;

	for (it = pGenotipo.begin(); it != pGenotipo.end(); it++) {

		capacidadeVertice = G->getCapacidade(*it);
		demandaVertice = G->getDemanda(*it);

		//Definindo o atendimento para a propria mediana
		capacidadeVertice -= demandaVertice;
		setAtendimento(*it, *it);

		selecioneVerticesCandidatos((*it), &candidatos, G);
		ordenaVerticesCandidatos(candidatos);

		while (capacidadeVertice > 0) {

			if (candidatos.empty()) {
				break;

			} else {
				c = candidatos.at(0);
				candidatos.erase(candidatos.begin());
				demandaVertice = G->getDemanda(c.idCandidato);
				if (capacidadeVertice - demandaVertice >= 0) {
					setAtendimento(*it, c.idCandidato);
					capacidadeVertice -= demandaVertice;
				}

			}

		}
	}

	numVertices = G->getNumVertices();

	for (int i = 0; i < numVertices; i++) {
		for (int j = 0; j < numVertices; j++) {
			atendimento = pMatrizDemanda->getItem(i, j).valor.intValor;
			this->fitness += (atendimento * (G->getDistancia(i, j)));
		}
	}

	pMatrizDemanda->reinicia();
	//std::cout << "Fitness Final: " << this->fitness << std::endl;
}

void Individuo::setAtendimento(int mediana, int vertice) {
	Item aux;
	aux.valor.intValor = 1;
	pMatrizDemanda->insereItem(aux, mediana, vertice);

}

void Individuo::selecioneVerticesCandidatos(int mediana,
		std::vector<candidato>* pCandidatos, Grafo* G) {

	int numColuns = pMatrizDemanda->getNumColunas();
	candidato aux;

	for (int i = 0; i < numColuns; i++) {
		if (pMatrizDemanda->getItem(mediana, i).valor.intValor == 0) {
			//Se o vertice não foi atendido por nenhuma mediana
			aux.idCandidato = i;
			aux.chave = G->getDistancia(mediana, i);
			pCandidatos->push_back(aux);
		}
	}

}

void Individuo::alteraAlelo(int alelo, int novoValor) {

	//std::cout << "Individuo antes:"<<std::endl;
	//this->show();

	//std::cout<<"Poscicao do alelo: " << alelo << "Novo alelo: " << novoValor << std::endl;

	if(!findAlelo(novoValor)){
		this->pGenotipo.erase(pGenotipo.begin() + alelo);
			this->pGenotipo.insert(pGenotipo.begin() + alelo, novoValor);
			if ((int) pGenotipo.size() > this->tamGenotipo) {
				std::cout << "Erro de insercao. O tamanho do genotipo é: "
						<< pGenotipo.size() << "Metodo : Individuo::alteraAlelo"<< std::endl;
				_exit(-1);
			}

	}else{
		std::cout << "Alelo pertecente: Nao sera inserido" <<std::endl;
	}
	//std::cout << "Individuo depois:"<<std::endl;
	//this->show();
}

bool Individuo::findAlelo(const int& alelo) const {
	return findInVector(this->pGenotipo, alelo);
}

std::vector<int>* Individuo::diferenca(const Individuo& i) const {

	std::vector<int> *alelos = new std::vector<int>();
	int tamGenotipo = this->tamGenotipo;

	for (int index = 0; index < tamGenotipo; index++) {

		if (!(i.findAlelo(this->pGenotipo.at(index)))) {
			/* Se o alelo nao estier no o
			 * outro individuo, insere no vector exchange			 */

			alelos->push_back(this->pGenotipo.at(index));
		}
	}

	return alelos;
}

int Individuo::getAlelo(const int& posicao) const {
	return this->pGenotipo.at(posicao);
}

int Individuo::getTamGenotipo() {
	return (this->tamGenotipo);
}

bool Individuo::operator ==(const Individuo& i) {
	int tamGenotpo = this->getTamGenotipo();
	int index;
	for (index = 0; index < tamGenotpo; index++) {
		if (this->getAlelo(index) != i.getAlelo(index)) {
			break;
		}
	}

	return (index == tamGenotpo);

}

Individuo::~Individuo() {
	//delete (pMatrizDemanda);
	//std::cout << "Executando Destruidor Individuo" << std::endl;

}

} /* namespace computacaoNatural */
